package cn.kgc.vue.shiro;

import cn.hutool.crypto.digest.MD5;
import cn.kgc.vue.commons.JWTToken;
import cn.kgc.vue.entity.RolePers;
import cn.kgc.vue.entity.UserRole;
import cn.kgc.vue.mapper.PermissionMapper;
import cn.kgc.vue.mapper.RolePersMapper;
import cn.kgc.vue.mapper.UserRoleMapper;
import cn.kgc.vue.utils.IpUtil;
import cn.kgc.vue.utils.JWTUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author 课工场
 * @date 2024/4/9
 * @description  自定义域对象 实现用户的认证和授权
 */
@Slf4j
public class CustomerRealm extends AuthorizingRealm {


    @Resource
    private UserRoleMapper userRoleMapper;
    @Resource
    private RolePersMapper rolePersMapper;
    @Resource
    private PermissionMapper permissionMapper;

    // UnsupportedTokenException    判定当前realm是否支持JWTToken
    @Override
    public boolean supports(AuthenticationToken token){

        return  token instanceof JWTToken;
    }

    @Resource
    private JWTUtil jwtUtil;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        Object primaryPrincipal = principals.getPrimaryPrincipal();  //  uid
        log.info("-----------------doGetAuthorizationInfo----------------------");
        log.info("primaryPrincipal={}",primaryPrincipal);

        //  根据用户的id 查询用户角色信息
        LambdaQueryWrapper<UserRole> lambda = new QueryWrapper<UserRole>().lambda();
        lambda.eq(UserRole::getUserId,primaryPrincipal);

        List<Integer> rids = userRoleMapper.selectList(lambda)
                .stream()
                .map(ur -> ur.getRoleId())
                .collect(Collectors.toList());
        // 根据角色信息 查询权限信息
        LambdaQueryWrapper<RolePers> lambda1 = new QueryWrapper<RolePers>().lambda();
        lambda1.in(RolePers::getRoleId,rids);

        Set<Integer> pids = rolePersMapper.selectList(lambda1)
                .stream()
                .map(rp -> rp.getPerId())
                .collect(Collectors.toSet());


        List<String> permissions = permissionMapper.selectBatchIds(pids)
                .stream()
                .filter(permission -> permission.getIsMenu() == 2)
                .map(permission -> permission.getPermission())
                .collect(Collectors.toList());

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addStringPermissions(permissions);

        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        JWTToken jwtToken = (JWTToken) token;
        String tk = (String) jwtToken.getPrincipal();

        // 判定当前token 在redis中是否存在
        String key = MD5.create().digestHex16(tk);

        if(!stringRedisTemplate.hasKey(key)){
            throw new RuntimeException("token失效");
        }

        // 判定token是否被盗用
        String redisIp = stringRedisTemplate.opsForValue().get(key);
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String ip = IpUtil.getIp(request);

        if (!StringUtils.equals(redisIp,ip)){
            throw  new RuntimeException("token盗用");
        }

        // 校验  token的真伪
        jwtUtil.verify(tk);


        // 解析token
        Map<String, Object> claims = jwtUtil.getClaims(tk);
        Object uid = claims.get("uid");

        // token 校验通过
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(uid,tk,this.getName());


        //   simpleAuthenticationInfo   tk1         token   tk2      tk1  == tk2
        return simpleAuthenticationInfo;
    }
}
